<?php

// +----------------------------------------------------------------------
// | 合同服务层
// +----------------------------------------------------------------------
// | 主要服务于合同
// +----------------------------------------------------------------------
// | 错误编码头 237xxx
// +----------------------------------------------------------------------
namespace common\service;


use common\exception\LogicException;
use common\model\ContractModel;
use common\model\ContractTemplateModel;
use common\model\OrderModel;
use common\model\OrderTaskListModel;
use common\model\ProjectMemberModel;
use common\model\ProjectModel;
use common\model\StepCategoryModel;
use common\model\UserModel;
use support\bootstrap\Redis;
use support\SingletonTrait;
use Webman\Stomp\Client;
use support\ErrorCode;
use think\Exception;
use Throwable;


class ContractService
{
    private $contractModel;


    // 单例工具
    use SingletonTrait;

    public function __construct()
    {
        $this->contractModel = model(ContractModel::class);
    }

    /**
     * 生成一个唯一的合同编号 和fdd合同编号
     * @return array
     * @throws \Exception
     */
    public function generateContractNumber()
    {
        $contractModel = model(ContractModel::class);
        $contractNumber = null;

        do {
            $contractNumber = date("YmdH") . random_int(11111, 99999);
            $exist = $contractModel->where(['number' => $contractNumber])->find();
        } while ($exist);

        do {
            $fddContractId = md5(create_uuid());
            $exist = $contractModel->where(['fdd_contract_id' => $fddContractId])->find();
        } while ($exist);

        return ['number' => $contractNumber, 'fdd_contract_id' => $fddContractId];
    }

    /**
     * 提交合同 返回签约地址
     * @param $data
     * @param $tenantId
     * @param $signer
     * @return array
     * @throws Exception
     * @throws Throwable
     */
    public function create($data, $tenantId, $signer)
    {
        $contractModel = model(ContractModel::class);
        $contractTemplateModel = model(ContractTemplateModel::class);
        $commonService = CommonService::instance("contract");
        $commonService->setCurrentModuleCode('contract');
        $fddService = FDDService::getInstance();
        $randomNumber = $this->generateContractNumber();
        $extraParam = $data['extra_param'] ?? [];
        $dynamicTables = $data['dynamic_tables'] ?? [];
        $businessMode = OptionsService::getInstance()->getBusinessMode();

        if (empty($data['name'])) {

            switch ($data['type']) {
                case "contract_type_framework":
                    $name = "框架合同";
                    /**
                     *
                     * 获取平台 租户
                     *
                     */

                    if ($businessMode == OptionsService::BUSINESS_MODE_TRIPARTITE) { //第三方平台方签订合同
                        $requestData = ['param' => ['filter' => [
                            'name' => "platform_tenant_settings"
                        ]]];
                        $platformTenantConfig = teamones_request('teamones-im', 'options/find', $requestData)['data'];
                        if (empty($platformTenantConfig)) {
                            throw new LogicException("missing platform tenant config", ErrorCode::MISSING_PLATFORM_TENANT_CONFIG);
                        }
                        $config = $platformTenantConfig['config'];
                        $data['from_tenant_id'] = $config['tenant_id'];
                    }
                    break;
                case "contract_type_project":
                    $name = "项目合同";
                    break;
                case "contract_type_order":
                    $name = "订单合同";
                    break;
                case "contract_type_cancel_contract":
                    $name = "取消合同合同";
                    break;
                case "contract_type_before_authsign":
                    $name = "授权自动签署";
                    break;
                default:
                    $name = "合同";
            }
            $data['name'] = $name . date("YmdHi");
        }


        $data['number'] = $randomNumber['number'];
        $data['fdd_contract_id'] = $randomNumber['fdd_contract_id'];


        if ($data['type'] != 'contract_type_before_authsign') {
            $template = $contractTemplateModel->where(['id' => $data['contract_template_id']])->find();

            if (empty($template)) {
                throw_strack_exception("contract template {$data['contract_template_id']} is not exist", ErrorCode::CONTRACT_TEMPLATE_NOT_EXIST);
            }
        }
        $requestData = ['param' => ["filter" => [
            "tenant_id" => $data['from_tenant_id'],
        ]]];
        $tenantAuthenticationPartyA = teamones_request('teamones-im', 'tenant_authentication/find', $requestData)['data'];
        $tenantAuthenticationPartyA = $tenantAuthenticationPartyA['tenant_authentication'] ?? null;
        if (empty($tenantAuthenticationPartyA) || $tenantAuthenticationPartyA['status'] != 'active' || $tenantAuthenticationPartyA['fdd_auth_status'] != 4) {
            throw_strack_exception("tenant authentication did not active", ErrorCode::TENANT_AUTHENTICATION_NOT_ACTIVATED);
        }
        if ($tenantAuthenticationPartyA['fdd_cert_status'] != 1) {
            throw_strack_exception("tenant cert did not active", ErrorCode::TENANT_CERTIFICATE_NOT_ACTIVATED);
        }
        $requestData = ['param' => ['filter' => [
            'id' => $data['from_tenant_id'],
        ]]];
        $tenant = teamones_request('teamones-im', 'tenant/find', $requestData)['data'];
        if (empty($tenant) || empty($tenant['fdd_customer_id'])) {
            throw_strack_exception("missing fdd_customer_id", ErrorCode::MISSING_FDD_CUSTOMER_ID);
        }
        $requestData = ['param' => ['filter' => [
            'id' => ['-eq', $data['to_tenant_id']]
        ]]];
        $partyB = teamones_request('teamones-im', 'tenant/find', $requestData)['data'];
        $requestData = ['param' => ["filter" => [
            "tenant_id" => $data['to_tenant_id'],
        ]]];
        $tenantAuthenticationPartyB = teamones_request('teamones-im', 'tenant_authentication/find', $requestData)['data'];
        $tenantAuthenticationPartyB = $tenantAuthenticationPartyB['tenant_authentication'] ?? null;

        /**
         * 签署框架合同时，如果对方没有认证，我方也可以先签署框架合同
         */
//        if (empty($tenantAuthenticationPartyB) || $tenantAuthenticationPartyB['status'] != 'active' || $tenantAuthenticationPartyB['fdd_auth_status'] != 4) {
//            throw_strack_exception("tenant authentication did not active", ErrorCode::TENANT_AUTHENTICATION_NOT_ACTIVATED, $tenantAuthenticationPartyB);
//        }
//        if ($tenantAuthenticationPartyB['fdd_cert_status'] != 1) {
//            throw_strack_exception("tenant cert did not active", ErrorCode::TENANT_CERTIFICATE_NOT_ACTIVATED);
//        }

        if (empty($partyB) || empty($partyB['fdd_customer_id'])) {
            throw_strack_exception("missing fdd_customer_id", ErrorCode::MISSING_FDD_CUSTOMER_ID);
        }


        // 只有授权签不需要提前生成合同
        if ($data['type'] == "contract_type_before_authsign") {
            $data['contract_template_id'] = 0;
            $data['start_time'] = $data['end_time'] = date("Y-m-d H:i:s");
        } else {


            $contractExampleData = $data;
            // 如果存在额外参数 那么把额外参数合并到合同填充数据里去
            if (array_key_exists('param', $data)) {
                unset($contractExampleData['param']);
                $contractExampleData = array_merge($contractExampleData, $data['param']);
            }
            $contractExampleData['start_time'] = date("Y年m月d日", strtotime($contractExampleData['start_time']));
            $contractExampleData['end_time'] = date("Y年m月d日", strtotime($contractExampleData['end_time']));
            // $contractExampleData['created'] = date("Y年m月d日");
            // 合同创建时间和签订时间保持一致
            $contractExampleData['created'] = $contractExampleData['start_time'];
            $parameterMap = $this->fillParameterMap($tenantAuthenticationPartyA, $tenantAuthenticationPartyB, $contractExampleData, $template['template_id'], $extraParam);
            APP_DEBUG && trace("contract fillParameterMap res", 'DEBUG');
            APP_DEBUG && trace(var_export($parameterMap, true), 'DEBUG');
            // 生成FDD合同
            $generateResult = $fddService->generateContract([
                'title' => $data['name'],
                'template_id' => $template['template_id'],
                'contract_id' => $data['fdd_contract_id'],
                'parameter_map' => $parameterMap,
                'dynamic_tables' => $dynamicTables
            ]);
        }


        $contractModel->startTrans();

        try {
            // 创建合同
            $contract = $contractModel->addItem($data);
            // 填充回去 fdd viewpdf_url download_url
            $newParam = $contract['param'];
            $newParam['viewpdf_url'] = $generateResult['viewpdf_url'] ?? "";
            $newParam['download_url'] = $generateResult['download_url'] ?? "";
            $contractModel->modifyItem([
                'id' => $contract['id'],
                'param' => $newParam
            ]);


            if ($data['type'] == 'contract_type_framework') {
                //三方平台更新租户的框架合同id
                if ($businessMode == OptionsService::BUSINESS_MODE_TRIPARTITE) {
                    $requestData = [
                        'data' => [
                            'id' => $data['to_tenant_id'],
                            'framework_agreement_contract_id' => $contract['id'],
                            'framework_agreement_status' => "wait_for_sign",
                        ]
                    ];
                    teamones_request('teamones-im', 'tenant/update', $requestData);
                } elseif ($businessMode == OptionsService::BUSINESS_MODE_BOTH_SIDES) {
                    if (empty($data['param']['supplier_id'])) {
                        throw new LogicException("supplier not exist", ErrorCode::SUPPLIER_NOT_EXIST);
                    }

                    //更新乙方的框架合同id
                    $requestData = [
                        'data' => [
                            'id' => $data['param']['supplier_id'],
                            'framework_agreement_contract_id' => $contract['id'],
                            'framework_agreement_status' => "wait_for_sign",
                        ]
                    ];
                    teamones_request('teamones-im', 'supplier/update', $requestData);
                }
            }

            // 自动签署 需要开通自动签署权限
//            $fddService->extSignAuto([
//                "transaction_id" => $data['fdd_transaction_id'],
//                'contract_id' => $data['fdd_contract_id'],
//                'customer_id' => $fddCustomerId,
//                'doc_title' => $data['name'],
//                'sign_keyword' => $tenantAuthentication['company_name'],
//            ]);


            $contractModel->commit();

        } catch (Throwable $e) {
            $contractModel->rollback();
            throw  $e;
        }

        //@update 2020年12月17日 和平台签署 不需要通知
        //通知供应商 发起了框架协议签署
//        if ($data['type'] == 'contract_type_framework') {
//            try {
//                $this->notifySupplierFrameworkContractSign($data['to_tenant_id'], $contract);
//            } catch (\Throwable $e) {
//                trace($e->getMessage() . PHP_EOL . $e->getTraceAsString(), "ERR");
//            }
//        }


        // 获取手动签署地址
        $signUrl = $this->extSign([
            'filter' => [
                'id' => $contract['id']
            ]
        ], $tenantId, $signer);
        return [
            'contract' => $contract,
            'sign_url' => $signUrl
        ];
    }

    /**
     * 手动签署合同
     * 返回手动签署地址
     * @param $param
     * @param $tenantId
     * @param $signer
     * @return mixed
     * @throws \Exception
     */
    public function extSign($param, $tenantId, $signer)
    {
        $contractModel = model(ContractModel::class);
        $contract = $contractModel->where($param['filter'])->find();
        $outhCustomerId = "";
        if (empty($contract)) {
            throw_strack_exception("missing contract ", ErrorCode::MISSING_CONTRACT);
        }
        $requestData = ['param' => ["filter" => [
            "tenant_id" => $tenantId,
        ]]];
        $tenantAuthentication = teamones_request('teamones-im', 'tenant_authentication/find', $requestData)['data'];
        $tenantAuthentication = $tenantAuthentication['tenant_authentication'];
        if (empty($tenantAuthentication) || $tenantAuthentication['status'] != 'active' || $tenantAuthentication['fdd_auth_status'] != 4) {
            throw_strack_exception("tenant authentication did not active", ErrorCode::TENANT_AUTHENTICATION_NOT_ACTIVATED);
        }
        if ($tenantAuthentication['fdd_cert_status'] != 1) {
            throw_strack_exception("tenant cert did not active", ErrorCode::TENANT_CERTIFICATE_NOT_ACTIVATED);
        }
        $requestData = ['param' => ['filter' => [
            'id' => $tenantId,
        ]]];
        $tenant = teamones_request('teamones-im', 'tenant/find', $requestData)['data'];
        if (empty($tenant) || empty($tenant['fdd_customer_id'])) {
            throw_strack_exception("missing fdd_customer_id", ErrorCode::MISSING_FDD_CUSTOMER_ID);
        }

        $contractTemplate = model(ContractTemplateModel::class)->where(['id' => $contract['contract_template_id']])->find();

        if (empty($contractTemplate) && $contract['type'] != "contract_type_before_authsign") {
            throw new LogicException("missing contract template data", ErrorCode::MISSING_CONTRACT_TEMPLATE_DATA);
        }

        $transactionId = md5(create_uuid());
        $modifyData = [
            'id' => $contract['id'],
        ];
        $checkKey = "";
        if ($contract['type'] == "contract_type_before_authsign") {
            // 授权给平台自动签署 目前只有平台需要开通自动签署功能 所以这里甲方/乙方是平台
            // 甲方
            $modifyData['fdd_party_a_transaction_id'] = $transactionId;
            $modifyData['party_a_signer'] = $signer;
            $checkKey = "party_a_signed_time";
        } else if ($contract['from_tenant_id'] == $tenant['id']) {
            // 甲方
            $modifyData['fdd_party_a_transaction_id'] = $transactionId;
            $modifyData['party_a_signer'] = $signer;
            $signKeyword = $contractTemplate['party_a_sign_keyword'];
            $checkKey = "party_a_signed_time";
        } else if ($contract['to_tenant_id'] == $tenant['id']) {
            // 乙方
            $modifyData['fdd_party_b_transaction_id'] = $transactionId;
            $modifyData['party_b_signer'] = $signer;
            $signKeyword = $contractTemplate['party_b_sign_keyword'];
            $checkKey = "party_b_signed_time";

        } else {
            throw_strack_exception("you don`t belong to this contract", ErrorCode::NOT_BELONG_TO_THIS_CONTRACT);
        }

        // 检测是否能进行签署合同操作
        // 签署用户身份验证 只有法人和被授权签署的人可以进行签署
        if ($signer !== 0) {
            $checkRes = $this->checkUserCanSignContract($tenantAuthentication, $signer, $tenant);
            if ($checkRes !== true) {
                $outhCustomerId = $checkRes;
            }
        }

        // 已经签署 直接返回查看地址
        if ($contract[$checkKey] > 0) {
            $jsonFields = json_decode($contract['param'], true);
            return $jsonFields['viewpdf_url'];
        }


        // 更新签署交易号
        $contractModel->save($modifyData);

        // 预设x-user-info得header到缓存中  把缓存key 暴露出去
        $code = "fdd_sign_callback_code" . $tenant['id'] . $contract['id'];
        Redis::set($code, request()->getXUserInfo(), 'EX', 60 * 60);

        $redirectUrlExtraParam = [
            'code' => $code,
        ];

        // 获取交易地址
        $fddService = FDDService::getInstance();
        if ($contract['type'] == "contract_type_before_authsign") {
            // 生成FDD合同
            $data = $fddService->beforeAuthSign([
                'contract_id' => $contract['fdd_contract_id'],
                'transaction_id' => $transactionId,
                'customer_id' => $tenant['fdd_customer_id'],
                'redirect_url_extra_param' => $redirectUrlExtraParam,
            ]);
            return $data;
        }

        return $fddService->extSign([
            "transaction_id" => $transactionId,
            'contract_id' => $contract['fdd_contract_id'],
            'doc_title' => $contract['name'],
            'customer_id' => $tenant['fdd_customer_id'],
            'sign_keyword' => $signKeyword,
            'redirect_url_extra_param' => $redirectUrlExtraParam,
            'outh_customer_id' => $outhCustomerId,
        ]);
    }

    /**
     * 采购合同列表
     * @param $param
     * @param $tenantId
     * @return array
     */
    public function getContractList($param, $tenantId)
    {
        $type = $param['type'] ?? 0;
        $filterTenantKey = "from_tenant_id";
        if ($type == 1) {
            $filterTenantKey = "to_tenant_id";
        }

        $filter = $param['filter'] ?? [];
        $order = $param['order'] ?? "contract.id DESC";
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];
        $filter['order.' . $filterTenantKey] = $tenantId;

        $businessMode = OptionsService::getInstance()->getBusinessMode();
        if ($businessMode === OptionsService::BUSINESS_MODE_TRIPARTITE) {
            $data['total'] = model(ContractModel::class)
                ->join("`order` ON `order`.id = contract.order_id AND contract.to_tenant_id = {$tenantId}")
                ->where($filter)
                ->count();
            $data['rows'] = model(ContractModel::class)
                ->join("`order` ON `order`.id = contract.order_id AND contract.to_tenant_id = {$tenantId}")
                ->where($filter)
                ->order($order)
                ->page($page[0], $page[1])
                ->field("contract.*")
                ->select();
        } else if ($businessMode === OptionsService::BUSINESS_MODE_BOTH_SIDES) {
            $data['total'] = model(ContractModel::class)
                ->join("`order` ON `order`.id = contract.order_id")
                ->where($filter)
                ->count();
            $data['rows'] = model(ContractModel::class)
                ->join("`order` ON `order`.id = contract.order_id")
                ->where($filter)
                ->order($order)
                ->page($page[0], $page[1])
                ->field("contract.*")
                ->select();
        } else {
            throw new LogicException("wrong business mode", ErrorCode::WRONG_BUSINESS_MODE);
        }

        $userIdData = $orderIdData = $tenantIdData = [];
        foreach ($data['rows'] as &$row) {
            $orderIdData[$row['order_id']] = $row['order_id'];
            $userIdData[$row['party_a_signer']] = $row['party_a_signer'];
            $userIdData[$row['party_b_signer']] = $row['party_b_signer'];
            $userIdData[$row['created_by']] = $row['created_by'];
            $tenantIdData[$row['from_tenant_id']] = $row['from_tenant_id'];
            $tenantIdData[$row['to_tenant_id']] = $row['to_tenant_id'];
//            $row['start_time'] = get_format_date($row['start_time'], 1);
//            $row['end_time'] = get_format_date($row['end_time'], 1);
//            $row['created'] = get_format_date($row['created'], 1);
//            $row['updated'] = get_format_date($row['created'], 1);
        }

        $orderData = model(OrderModel::class)
            ->where([
                'id' => ['IN', implode(',', $orderIdData)]
            ])
            ->select();

        if (count($orderData) > 0) {
            foreach ($orderData as $orderRow) {
                $userIdData[$orderRow['created_by']] = $orderRow['created_by'];
                $tenantIdData[$orderRow['from_tenant_id']] = $orderRow['from_tenant_id'];
                $tenantIdData[$orderRow['to_tenant_id']] = $orderRow['to_tenant_id'];
            }
            $orderData = array_column($orderData, null, 'id');
        }


        // 创建者查询

        $userData = model(UserModel::class)
            ->field("json,uuid", true)
            ->where(['id' => ['IN', implode(',', $userIdData)]])
            ->select();
        $userData = array_column($userData, null, 'id');
        //租户信息查询
        $requestData = ['param' => [
            'filter' => [
                "tenant_id" => [
                    "-in", implode(',', $tenantIdData)
                ]
            ],
            "fields" => "tenant_id as id,company_name,person_in_charge,person_in_charge_phone,person_in_charge_email,telephone_number"
        ]];
        $tenantData = teamones_request('teamones-im', 'tenant_authentication/select', $requestData)['data'];
        $tenantData = array_column($tenantData['rows'], null, 'id');

        if (count($orderData) > 0) {
            foreach ($orderData as $orderId => &$orderRow) {
                $orderRow['from_tenant'] = $tenantData[$orderRow['from_tenant_id']] ?? null;
                $orderRow['to_tenant'] = $tenantData[$orderRow['to_tenant_id']] ?? null;
                $orderRow['creator'] = $userData[$orderRow['created_by']] ?? null;
            }
        }

        foreach ($data['rows'] as &$row) {
            $row['from_tenant'] = $tenantData[$row['from_tenant_id']] ?? null;
            $row['to_tenant'] = $tenantData[$row['to_tenant_id']] ?? null;
            $row['creator'] = $userData[$row['created_by']] ?? null;
            $row['party_a_signer'] = $userData[$row['party_a_signer']] ?? null;
            $row['party_b_signer'] = $userData[$row['party_b_signer']] ?? null;
            $row['order'] = $orderData[$row['order_id']] ?? null;
        }

        return $data;
    }

    /**
     * 合同详情
     * @param $param
     * @return mixed
     * @throws \Exception
     */
    public function getContractDetail($param)
    {
        $commonService = CommonService::instance("contract");
        $data = $commonService->find($param);
        $data['order'] = model(OrderModel::class)->where(['id' => $data['order_id']])->find();
        if (!empty($data['order'])) {
            $data['order'] = CommonService::getFormatAttrData(model(OrderModel::class), $data['order']);
        }
        // 创建者查询
        $userIds = [$data['created_by'], $data['party_a_signer'], $data['party_b_signer']];
        $userData = model(UserModel::class)->field("json,uuid", true)->where(['id' => ['IN', implode(',', $userIds)]])->select();
        $userData = array_column($userData, null, 'id');
        //租户信息查询
        $tenantIdData = [$data['from_tenant_id'], $data['to_tenant_id']];
        $requestData = ['param' => [
            'filter' => [
                "tenant_id" => [
                    "-in", implode(',', $tenantIdData)
                ]
            ],
            "fields" => "tenant_id as id,company_name,person_in_charge,person_in_charge_phone,person_in_charge_email,company_web_site,unified_social_credit_code,company_registered_address,bank_of_deposit,bank_account"
        ]];
        $tenantData = teamones_request('teamones-im', 'tenant_authentication/select', $requestData)['data'];
        $tenantData = array_column($tenantData['rows'], null, 'id');
        $data['from_tenant'] = $tenantData[$data['from_tenant_id']] ?? null;
        $data['to_tenant'] = $tenantData[$data['to_tenant_id']] ?? null;
        $data['creator'] = $userData[$data['created_by']] ?? null;
        $data['party_a_signer'] = $userData[$data['party_a_signer']] ?? null;
        $data['party_b_signer'] = $userData[$data['party_b_signer']] ?? null;
        return $data;
    }

    /**
     * 查询订单合同签署人
     * @param $orderId
     * @return int[]
     */
    public function getOrderSigner($orderId)
    {
        $businessMode = OptionsService::getInstance()->getBusinessMode();
        $order = model(OrderModel::class)->find($orderId);
        if ($businessMode === OptionsService::BUSINESS_MODE_TRIPARTITE) {
            $contractPartyB = $this->contractModel->where(['order_id' => $orderId, 'to_tenant_id' => $order['to_tenant_id']])->find();
            $contractPartyA = $this->contractModel->where(['order_id' => $orderId, 'to_tenant_id' => $order['from_tenant_id']])->find();
            $contractPartyASigner = $contractPartyA['party_b_signer'] ?? 0;
            $contractPartyBSigner = $contractPartyB['party_b_signer'] ?? 0;
        } else if ($businessMode === OptionsService::BUSINESS_MODE_BOTH_SIDES) {
            // 双方模式 只需要双方签署合同
            $contract = $this->contractModel->where(['order_id' => $orderId])->find();
            $contractPartyASigner = $contract['party_a_signer'];
            $contractPartyBSigner = $contract['party_b_signer'];
        } else {
            throw new LogicException("wrong business mode", ErrorCode::WRONG_BUSINESS_MODE);
        }
        return ['a_signer' => $contractPartyASigner, 'b_signer' => $contractPartyBSigner];
    }

    /**
     * 处理合同签署动态
     * @param $contract
     * @param $transactionId
     * @throws Exception
     * @throws Throwable
     */
    public function handleContractSignChange($contract, $transactionId)
    {
        APP_DEBUG && trace(__METHOD__ . '#' . $contract['id'], "DEBUG");
        $contractModel = model(ContractModel::class);
        $contract = $contractModel->find($contract['id']);
        $tenantService = TenantService::getInstance();
        $platformTenantId = $tenantService->getPlatformTenantId();
        $businessMode = OptionsService::getInstance()->getBusinessMode();

        /**
         *
         * 判断当前回调 是合同的哪一方签署
         *
         */
        $isPartyA = $isPartyBSignAction = false;

        if ($contract['fdd_party_a_transaction_id'] == $transactionId) {
            $isPartyA = true;
        } else if ($contract['fdd_party_b_transaction_id'] == $transactionId) {
            $isPartyBSignAction = true;
        } else {
            throw new LogicException("invalid contract callback", ErrorCode::INVALID_CONTRACT_CALLBACK);
        }

        $orderContractSignSwitch = true;

        $contractModel->startTrans();
        try {
            switch ($contract['type']) {
                case "contract_type_framework": // 框架合同

                    //检查甲方合同是否没有签署 如果甲方是平台 那么自动签署
                    if ($businessMode == OptionsService::BUSINESS_MODE_TRIPARTITE && $contract['from_tenant_id'] == $platformTenantId && ($contract['party_a_signed_time'] <= 0)) {
                        $contractTenantKey = 'from_tenant_id';
                        $contractFilter = ['filter' => ['id' => $contract['id']]];
                        $this->extSignAuto($contractFilter, $contractTenantKey);
                    }

                    if ($contract['party_a_signed_time'] > 0 && $contract['party_b_signed_time'] > 0) {
                        // 判断甲乙双方是否都签过合同


                        if ($businessMode == OptionsService::BUSINESS_MODE_TRIPARTITE) {
                            // 更新供应商框架合同签署状态为已完成
                            $requestData = [
                                'data' => [
                                    'id' => $contract['to_tenant_id'],
                                    'framework_agreement_status' => "signed",
                                ],
                            ];
                            teamones_request('teamones-im', 'tenant/update', $requestData);
                        } elseif ($businessMode == OptionsService::BUSINESS_MODE_BOTH_SIDES) {
                            $contractParam = json_decode($contract['param'], true);
                            $supplierId = $contractParam['supplier_id'];
                            //更新乙方的框架合同id
                            $requestData = [
                                'data' => [
                                    'id' => $supplierId,
                                    'framework_agreement_status' => "signed",
                                ]
                            ];
                            teamones_request('teamones-im', 'supplier/update', $requestData);
                        }
                    }


                    break;
                case "contract_type_order":

                    $businessMode = OptionsService::getInstance()->getBusinessMode();
                    APP_DEBUG && trace("contract_type_order got in", "DEBUG");
                    /**
                     * 查询订单
                     *
                     * 更新订单签署状态
                     *
                     * 生成任务
                     *
                     */
                    $order = model(OrderModel::class)->where(['id' => $contract['order_id']])->find();
                    if (empty($order)) {
                        throw new LogicException("contract missing order", ErrorCode::CONTRACT_MISSING_ORDER);
                    }
                    if ($order['status'] !== 'wait_for_sign') {
                        // 拦截签署异步通知的 二次通知 避免重复操作订单
                        throw new LogicException('order current status[' . $order['status'] . ']can`t do this operation', ErrorCode::ORDER_CANNOT_PERFORM_THIS_OPERATION);
                    }

                    $isContractSignDone = false;
                    $contractPartyASigner = $contractPartyBSigner = 0;
                    if ($businessMode === OptionsService::BUSINESS_MODE_TRIPARTITE) {
                        $contractPartyB = $contractModel->where(['order_id' => $order['id'], 'to_tenant_id' => $order['to_tenant_id']])->find();
                        $contractPartyA = $contractModel->where(['order_id' => $order['id'], 'to_tenant_id' => $order['from_tenant_id']])->find();
                        $isContractSignDone = !empty($contractPartyA) && !empty($contractPartyB) && $contractPartyA['status'] == "signed" && $contractPartyB['status'] == "signed";

                        $contractPartyASigner = $contractPartyA['party_b_signer'] ?? 0;
                        $contractPartyBSigner = $contractPartyB['party_b_signer'] ?? 0;

                        if (!$isContractSignDone) {
                            // 乙方签署合同之后 检查是否没有生成甲方合同
                            if (!empty($contractPartyB) && empty($contractPartyA) && $isPartyBSignAction) {
                                APP_DEBUG && trace("generateOrderContract", "DEBUG");
                                // 如果是乙方签署完成 那么生成订单甲方的订单合同
                                $orderService = OrderService::getInstance();
                                $order['start_time'] = get_format_date($order['start_time'], 1);
                                $order['end_time'] = get_format_date($order['end_time'], 1);
                                $orderService->generateOrderContract($order, $order['from_tenant_id'], 0);
                                try {
                                    // 通知甲方 乙方已经确认订单并且签署完成
                                    $orderService->notifyPartyASupplierProcessOrderResult($order['id'], true);
                                } catch (Throwable $e) {
                                    trace(format_exception_info($e), 'ERR');
                                }
                            }

                            // 两份合同的乙方签署合同之后 平台开始自动签署合同
                            if (!empty($contractPartyA) && !empty($contractPartyB)) {
                                // 检查甲方合同是否没有签署
                                if ($contractPartyA['status'] == "wait_for_sign" && $contractPartyA['party_a_signed_time'] == 0 && $contractPartyB['party_b_signed_time'] > 0) {
                                    APP_DEBUG && trace("auto extsign contract partyA", "DEBUG");
                                    $contractFilter = ['filter' => ['id' => $contractPartyA['id']]];
                                    $this->extSignAuto($contractFilter, "from_tenant_id");
                                }
                                // 检查乙方合同是否没有签署
                                if ($contractPartyB['status'] == "wait_for_sign" && $contractPartyB['party_a_signed_time'] == 0 && $contractPartyA['party_b_signed_time'] > 0) {
                                    APP_DEBUG && trace("auto extsign contract partyA", "DEBUG");
                                    $contractFilter = ['filter' => ['id' => $contractPartyB['id']]];
                                    $this->extSignAuto($contractFilter, "from_tenant_id");
                                }
                            }
                        }

                    } else if ($businessMode === OptionsService::BUSINESS_MODE_BOTH_SIDES) {
                        // 双方模式 只需要双方签署合同
                        $isContractSignDone = $contract['party_a_signed_time'] > 0 && $contract['party_b_signed_time'];
                        $contractPartyASigner = $contract['party_a_signer'];
                        $contractPartyBSigner = $contract['party_b_signer'];
                    } else {
                        throw new LogicException("wrong business mode", ErrorCode::WRONG_BUSINESS_MODE);
                    }


                    // 订单为待签署&&双方合同都签署之后 进行订单发送任务
                    if ($isContractSignDone && $order['status'] === 'wait_for_sign') {
                        APP_DEBUG && trace("contract all signed", "DEBUG");
                        // 正式扣款
                        if ($businessMode === OptionsService::BUSINESS_MODE_TRIPARTITE) {
                            $requestData = [
                                'id' => $order['id'],
                                'tenant_id' => $order['from_tenant_id'],
                                'money' => $order['money'],
                                'belong_system' => C("belong_system"),
                            ];
                            teamones_request('teamones-im', 'user/order_deduct', $requestData);
                        }
                        // 发布任务
                        //订单甲方签署完 那么发布任务
                        $orderModel = model(OrderModel::class);
                        $flag = $orderModel->modifyItem([
                            'id' => $order['id'],
                            'status' => "inprogress"
                        ]);
                        if (!$flag && $orderModel->getErrorCode() != \think\exception\ErrorCode::NO_DATA_HAS_BEEN_CHANGED) {
                            throw new LogicException("update order status failure", ErrorCode::ORDER_STATUS_UPDATE_FAILURE);
                        }
                        $orderTaskList = model(OrderTaskListModel::class)->where(['order_id' => $order['id'], 'status' => OrderTaskListModel::STATUS_DEFAULT])->select();
                        $orderTaskList = array_column($orderTaskList, null, 'task_id');
                        $tenantId = $order['to_tenant_id'];
                        $user = model(UserModel::class)->find($contractPartyBSigner);
                        $taskService = TaskService::getInstance();
                        $allocationTasksResult = [];
                        foreach ($orderTaskList as $taskItem) {
                            $allocationTasksResult['outside'][] = $taskService->setStepTaskExecutor($taskItem['task_id'], $contractPartyASigner, $user['id'], $tenantId, 'outside');
                        }
                        // 外包分配任务
                        $shadowProject = [];

                        if ($allocationTasksResult['outside']) {
                            foreach ($allocationTasksResult['outside'] as $allocationTask) {
                                $shadowTaskRes = $allocationTask['shadow_task'];
                                $fromTask = $shadowTaskRes['from'];
                                $toTask = $shadowTaskRes['to'];
                                // 写入真实任务id 到任务清单中
                                model(OrderTaskListModel::class)->save([
                                    'id' => $orderTaskList[$fromTask['id']]['id'],
                                    'real_task_id' => $toTask['id']
                                ]);
                                if (empty($shadowProject)) {
                                    $shadowProject = $allocationTask['shadow_project'] ?? [];
                                }
                            }

                            // 把乙方签署人加入项目成员中去
                            if (!empty($shadowProject)) {
                                $this->addOrderSignerToProjectMember($contractPartyBSigner, $shadowProject['id']);
                            }

                            // 追加订单module_code
//                            $orderParam = json_decode($order['param'], true);
//                            $projectService = new ProjectService();
//                            $projectService->appendModuleCodes($shadowProject['id'], [$orderParam['module_code'] ?? '']);

                            // 复制项目配置
                            if (!empty($shadowProject)) {
                                $projectService = ProjectService::getInstance();
                                $projectService->copyProjectTemplateRelateConfig($shadowProject['id']);
                            }

                        }
                        // 生成任务依赖文件传输列表
                        $orderService = OrderService::getInstance();
                        Client::send('generate_order_task_file_list', build_queue_data_with_xu(['order_id' => $order['id'], 'user_id' => $contractPartyBSigner,]), 3);
//                        $orderService->generatedOrderTaskFileList($order['id'], $contractPartyASigner);
                        // 取消其他相同任务订单
                        $orderService->cancelOtherSameOrder($order['id']);
                        // 组装播放列表
//                        $orderService->generatePlaylistPackage($order['id']);

                        // 更新订单文件状态
                        $orderService->updateOrderFileStatus($order['id']);
                    }


                    break;
                case "contract_type_cancel_contract":
                    APP_DEBUG && trace("contract_type_cancel_contract got in", "DEBUG");

                    /**
                     * 查询原合同
                     *
                     * 标记原合同提前终止
                     *
                     */

                    // 判断甲乙双方是否都签过合同
                    if ($contract['status'] == "signed") {

                        $contractParam = json_decode($contract['param'], true);
                        $sourceContract = $contractModel->find($contractParam['id']);
                        $sourceContract['status'] = 'early_terminated';
                        $flag = $contractModel->save($sourceContract);

                        if (!$flag) {
                            throw new LogicException("update source contract status failure", ErrorCode::SOURCE_CONTRACT_STATUS_UPDATE_FAILURE);
                        }
                    }
                    break;
                case "contract_type_before_authsign":
                    // todo 回调调试
                    // 授权签只需要甲方授权方签署
                    // 乙方为平台方 自动签署
                    $requestData = ['param' => ['filter' => [
                        'tenant_id' => $contract['to_tenant_id'],
                    ]]];
                    $ta = teamones_request('teamones-im', 'tenant_authentication/find', $requestData);
                    $ta = $ta['data']['tenant_authentication'];
                    model(ContractModel::class)->save([
                        'id' => $contract['id'],
                        'status' => "signed",
                        "party_b_signed_time" => time(),
                        'party_b_signer' => $ta['created_by'],
                    ]);
                    break;
            }
            $contractModel->commit();
        } catch (Throwable $e) {
            $contractModel->rollback();
            throw $e;
        }


    }


    /**
     * 取消合同
     * @param $tenantId
     * @param $param
     * @return array
     * @throws Exception
     * @throws Throwable
     */
    public function cancelContract($tenantId, $param)
    {
        $contractModel = model(ContractModel::class);
        $contract = $contractModel->findData($param);
        if (empty($contract)) {
            throw_strack_exception("can`t found contract", ErrorCode::CONTRACT_NOT_FOUND);
        }

        $contractTenantId = [$contract['from_tenant_id'], $contract['to_tenant_id']];
        if (in_array($tenantId, $contractTenantId)) {
            throw_strack_exception("you don`t belong to this contract", ErrorCode::NOT_BELONG_TO_THIS_CONTRACT);
        }

        /**
         *
         * 取消合同 发起方为乙方
         *
         */

        // 组装param
        $cancelContractParam = $contract;
        $cancelContractParam['cancel_reason'] = $param['cancel_reason'];

        $contractTemplate = $this->getCancelContractTemplate();
        $contractData = [
            "name" => "取消{$contract['name']} 的合同" . date("YmdHi"),
            'contract_template_id' => $contractTemplate['id'],
            'from_tenant_id' => $tenantId,
            'to_tenant_id' => $contract['to_tenant_id'] != $tenantId ? $contract['to_tenant_id'] : $contract['from_tenant_id'],
            'start_time' => time(),
            'end_time' => strtotime("+ 1 years"),
            'param' => $cancelContractParam,
            'project_id' => $contract['project_id'],
            'type' => 'contract_type_cancel_contract',
            'order_id' => 0,
        ];
        // 创建取消合同合同
        $cancelContract = $this->create($contractData, $tenantId, fill_created_by());

        // 修改原合同状态 放在合同签署回调里
        //            $contractModel->save([
//                'id' => $contract['id'],
//                'status' => "early_terminated"
//            ]);
        return $cancelContract;

    }

    /**
     * 查询取消合同模板
     * @return array
     */
    private function getCancelContractTemplate()
    {
        $optionsService = OptionsService::getInstance();
        $templateId = $optionsService->getOptionsConfigItemData('order_contract_config', 'cancel_contract_template_id');
        $template = model(ContractModel::class)->find($templateId);
        if (empty($template)) {
            throw new LogicException("missing cancel contract template config", ErrorCode::MISSING_CANCEL_CONTRACT_TEMPLATE_CONFIG);
        }
        return $template;
    }

    /**
     * 合同状态查询
     * @param $param
     * @return array
     * @throws \Exception
     */
    public function status($param)
    {
        $contract = model(ContractModel::class)->findData($param);

        if (empty($contract)) {
            throw new LogicException("can`t found match record", ErrorCode::NO_MATCH_RECORD);
        }
        $fddService = FDDService::getInstance();

        $contract['fdd_contract_status'] = $fddService->contractStatus($contract['fdd_contract_id']);
        return $contract;
    }

    /**
     * 填充合同参数列表
     * @param array $tenantAuthenticationPartyA
     * @param array $tenantAuthenticationPartyB
     * @param array $contractData
     * @param string $contractTemplateId
     * @param array $extraParam
     * @return array
     */
    private function fillParameterMap(array $tenantAuthenticationPartyA, array $tenantAuthenticationPartyB, array $contractData, string $contractTemplateId, array $extraParam = [])
    {
        $fddService = FDDService::getInstance();
        $keys = $fddService->getContractTemplateKeys($contractTemplateId);

        $tenantAuthenticationPartyAValueMap = [];
        foreach ($tenantAuthenticationPartyA as $key => $value) {
            // 处理特殊清空 银行账户与开户行连接
            if ($key == 'bank_of_deposit' && isset($tenantAuthenticationPartyA['bank_subbranch'])) {
                $tenantAuthenticationPartyAValueMap['party_a_bank_of_deposit_with_bank_subbranch'] = $value . $tenantAuthenticationPartyA['bank_subbranch'];
                continue;
            }

            $tenantAuthenticationPartyAValueMap["party_a_" . $key] = $value;
        }

        $tenantAuthenticationPartyBValueMap = [];
        foreach ($tenantAuthenticationPartyB as $key => $value) {
            // 处理特殊清空 银行账户与开户行连接
            if ($key == 'bank_of_deposit' && isset($tenantAuthenticationPartyB['bank_subbranch'])) {
                $tenantAuthenticationPartyBValueMap['party_b_bank_of_deposit_with_bank_subbranch'] = $value . $tenantAuthenticationPartyB['bank_subbranch'];
                continue;
            }
            $tenantAuthenticationPartyBValueMap["party_b_" . $key] = $value;
        }

        $contractMap = [];
        foreach ($contractData as $key => $value) {
            $contractMap["contract_" . $key] = $value;
        }

        $valueMap = array_merge($tenantAuthenticationPartyAValueMap, $tenantAuthenticationPartyBValueMap, $contractMap, $extraParam);
        $res = [];
        foreach ($keys as $key) {
            $res[$key] = $valueMap[$key] ?? "";
        }
        return $res;
    }

    /**
     * 获得合同列表 基本的甲乙双方 签约人
     * @param $param
     * @return array
     * @throws \Exception
     */
    public function getContractNormalList($param)
    {
        $filter = $param['filter'] ?? [];
        $page = $param['page'] ?? [1, C("database.database_max_select_rows")];
        $order = $param['order'] ?? "contract.id DESC";
        $total = model(ContractModel::class)->where($filter)->count();
        $rows = model(ContractModel::class)
            ->where($filter)
            ->order($order)
            ->page($page[0], $page[1])
            ->select();
        $userIdList = array_column($rows, 'created_by', 'created_by');
        $userIdList = array_merge($userIdList, array_column($rows, 'party_a_signer', 'party_a_signer'));
        $userIdList = array_merge($userIdList, array_column($rows, 'party_b_signer', 'party_b_signer'));
        $userIdList = array_unique($userIdList);
        $tenantIdList = array_column($rows, 'from_tenant_id', 'from_tenant_id');
        $tenantIdList = array_merge($tenantIdList, array_column($rows, 'to_tenant_id', 'to_tenant_id'));

        $userList = model(UserModel::class)->where(['id' => ['IN', implode(',', $userIdList)]])->select();
        $userMap = array_column($userList, null, 'id');
        $requestData = ['param' => [
            'filter' => [
                "tenant_id" => [
                    "-in", implode(',', $tenantIdList)
                ]
            ],
            "fields" => "tenant_id as id,company_name,person_in_charge,person_in_charge_phone,person_in_charge_email,telephone_number"
        ]];
        $tenantAuthList = teamones_request('teamones-im', 'tenant_authentication/select', $requestData)['data'];
        $tenantAuthMap = array_column($tenantAuthList['rows'], null, 'id');

        foreach ($rows as &$row) {
            $row = CommonService::getFormatAttrData(model(ContractModel::class), $row);
            $row['created_by'] = $userMap[$row['created_by']] ?? null;
            if (!empty($row['created_by'])) {
                $row['created_by'] = CommonService::getFormatAttrData(model(UserModel::class), $row['created_by']);
            }
            $row['party_a_signer'] = $userMap[$row['party_a_signer']] ?? null;
            if (!empty($row['party_a_signer'])) {
                $row['party_a_signer'] = CommonService::getFormatAttrData(model(UserModel::class), $row['party_a_signer']);
            }
            $row['party_b_signer'] = $userMap[$row['party_b_signer']] ?? null;
            if (!empty($row['party_b_signer'])) {
                $row['party_b_signer'] = CommonService::getFormatAttrData(model(UserModel::class), $row['party_b_signer']);
            }
            $row['from_tenant_authentication'] = $tenantAuthMap[$row['from_tenant_id']] ?? null;
            $row['to_tenant_authentication'] = $tenantAuthMap[$row['to_tenant_id']] ?? null;
        }

        return ['total' => $total, 'rows' => $rows];
    }

    /**
     * 查询订单合同列表
     * @param array $orderIds
     * @param $tenantId
     * @return array|false|mixed|string
     */
    public function getOrderContractList(array $orderIds, $tenantId)
    {
        $businessMode = OptionsService::getInstance()->getBusinessMode();
        if ($businessMode === OptionsService::BUSINESS_MODE_TRIPARTITE) {
            $contractFilter = [
                ['order_id' => ['IN', implode(',', $orderIds)],],
                ['order_id' => ['GT', 0],],
                ['to_tenant_id' => $tenantId],
                '_logic' => "and",
            ];
        } else if ($businessMode === OptionsService::BUSINESS_MODE_BOTH_SIDES) {
            $contractFilter = [
                ['order_id' => ['IN', implode(',', $orderIds)],],
                ['order_id' => ['GT', 0],],
                '_logic' => "and",
            ];
        } else {
            throw new LogicException("wrong business mode", ErrorCode::WRONG_BUSINESS_MODE);
        }
        return model(ContractModel::class)->where($contractFilter)->select();
    }

    /**
     * 查询订单合同
     * @param $id
     * @param $tenantId
     * @return array|false|mixed|string|null
     */
    public function getOrderContract($id, $tenantId)
    {
        $businessMode = OptionsService::getInstance()->getBusinessMode();
        if ($businessMode === OptionsService::BUSINESS_MODE_TRIPARTITE) {
            $contractFilter = [
                'order_id' => $id,
                'to_tenant_id' => $tenantId,
            ];
        } else if ($businessMode === OptionsService::BUSINESS_MODE_BOTH_SIDES) {
            $contractFilter = [
                'order_id' => $id,
            ];
        } else {
            throw new LogicException("wrong business mode", ErrorCode::WRONG_BUSINESS_MODE);
        }
        return model(ContractModel::class)->where($contractFilter)->find();
    }

    /**
     * 获得框架协议签署提醒模板id
     * @return int
     */
    private function getNotifySupplierFrameworkContractSignTemplateId()
    {
        $optionsService = OptionsService::getInstance();
        $messageTemplateId = $optionsService->getOptionsConfigItemData("message_notify_template_config", "supplier_framework_contract_sign_template_id");
        return (int)$messageTemplateId;
    }

    /**
     * 自动签署合同 返回手动签署地址
     * @param $param
     * @param $contractPartyKey
     * @param int $signer
     * @return true
     * @throws \Exception
     */
    public function extSignAuto($param, $contractPartyKey, $signer = 0)
    {
        $contractModel = model(ContractModel::class);
        $contract = $contractModel->where($param['filter'])->find();
        if (empty($contract)) {
            throw_strack_exception("missing contract ", ErrorCode::MISSING_CONTRACT);
        }
        $tenantId = $contract[$contractPartyKey];
        $requestData = ['param' => ["filter" => [
            "tenant_id" => $tenantId,
        ]]];
        $tenantAuthentication = teamones_request('teamones-im', 'tenant_authentication/find', $requestData)['data'];
        $tenantAuthentication = $tenantAuthentication['tenant_authentication'];
        if (empty($tenantAuthentication) || $tenantAuthentication['status'] != 'active' || $tenantAuthentication['fdd_auth_status'] != 4) {
            throw_strack_exception("tenant authentication did not active", ErrorCode::TENANT_AUTHENTICATION_NOT_ACTIVATED);
        }
        if ($tenantAuthentication['fdd_cert_status'] != 1) {
            throw_strack_exception("tenant cert did not active", ErrorCode::TENANT_CERTIFICATE_NOT_ACTIVATED);
        }

        // 如果没有提供签署人 那么就获取租户认证的创建人
        if ($signer == 0) {
            $userService = UserService::getInstance();
            $user = $userService->getUserByUnionId($tenantAuthentication['created_by'], true);
            $signer = $user['id'];
        }
        $requestData = ['param' => ['filter' => [
            'id' => $tenantId,
        ]]];
        $tenant = teamones_request('teamones-im', 'tenant/find', $requestData)['data'];
        if (empty($tenant) || empty($tenant['fdd_customer_id'])) {
            throw_strack_exception("missing fdd_customer_id", ErrorCode::MISSING_FDD_CUSTOMER_ID);
        }

        $contractTemplate = model(ContractTemplateModel::class)->where(['id' => $contract['contract_template_id']])->find();

        if (empty($contractTemplate) && $contract['type'] != "contract_type_before_authsign") {
            throw new LogicException("missing contract template data", ErrorCode::MISSING_CONTRACT_TEMPLATE_DATA);
        }

        $transactionId = md5(create_uuid());
        $modifyData = [
            'id' => $contract['id'],
        ];
        if ($contractPartyKey == "from_tenant_id") {
            // 甲方
            $modifyData['fdd_party_a_transaction_id'] = $transactionId;
            $modifyData['party_a_signer'] = $signer;
            $signKeyword = $contractTemplate['party_a_sign_keyword'];
        } else if ($contractPartyKey == "to_tenant_id") {
            // 乙方
            $modifyData['fdd_party_b_transaction_id'] = $transactionId;
            $modifyData['party_b_signer'] = $signer;
            $signKeyword = $contractTemplate['party_b_sign_keyword'];

        } else {
            throw_strack_exception("you don`t belong to this contract", ErrorCode::NOT_BELONG_TO_THIS_CONTRACT);
        }
        APP_DEBUG && trace(var_export($modifyData, true), "DEBUG");

        // 更新签署交易号
        $contractModel->save($modifyData);


        $code = "fdd_sign_callback_code" . $contractPartyKey . $contract['id'];
        Redis::set($code, request()->getXUserInfo(), 'EX', 60 * 60);

        $redirectUrlExtraParam = [
            'code' => $code,
        ];

        // 获取交易地址
        $fddService = FDDService::getInstance();

        $param = [
            'transaction_id' => $transactionId,
            'contract_id' => $contract['fdd_contract_id'],
            'customer_id' => $tenant['fdd_customer_id'],
            'doc_title' => $contract['name'],
            'sign_keyword' => $signKeyword,
            'redirect_url_extra_param' => $redirectUrlExtraParam,
        ];
        return $fddService->extSignAuto($param);

    }

    /**
     * 合同过期检查
     * 标记所有过期的合同为已过期
     * 框架合同需要重置租户的框架协议签署状态
     * @return int
     * @throws \Exception
     */
    public function checkContractDate()
    {
        $contracts = model(ContractModel::class)
            ->where([
                'status' => 'signed',
                'end_time' => ['LT', time()],
            ])
            ->field("id,type,to_tenant_id")
            ->select();

        $contractIdList = array_column($contracts, 'id');

        if (count($contractIdList) > 0) {

            $contracts = array_group_by($contracts, 'type');
            if (isset($contracts['contract_type_framework']) && count($contracts['contract_type_framework']) > 0) {
                $frameworkContractsGroupByToTenant = array_group_by($contracts['contract_type_framework'], 'to_tenant_id');
                foreach ($frameworkContractsGroupByToTenant as $toTenantId => $tenantContracts) {
                    // 检查当前租户是否存在已经签署的框架协议 除开已过期的合同
                    $count = model(ContractModel::class)
                        ->where([
                            'to_tenant_id' => $toTenantId,
                            'status' => 'signed',
                            'end_time' => ['GT', time()],
                            'type' => 'contract_type_framework',
                        ])->count();
                    if (intval($count) == 0) {
                        // 标记租户需要重新签署框架协议 to_be_submitted 等待提交
                        $requestData = ['data' => [
                            'id' => $toTenantId,
                            'framework_agreement_status' => "expired",
                        ]];
                        teamones_request('teamones-im', 'oauth/mark_tenant_framework_agreement_status', $requestData);
                        //TODO::判断平台模式
                    }
                }
            }

            return model(ContractModel::class)
                ->where(['id' => ['IN', implode(',', $contractIdList)]])
                ->save([
                    'status' => 'expired',
                ]);
        }

        return 0;

    }

    /**
     * 检查用户能否进行签署操作
     * @param $tenantAuthentication
     * @param $signer
     * @param $tenant
     * @return bool|mixed
     * @throws \Exception
     */
    public function checkUserCanSignContract($tenantAuthentication, $signer, $tenant)
    {
        $signerUser = model(UserModel::class)->find($signer);
        if ($tenantAuthentication['legal_phone'] != $signerUser['phone']) {
            // 当前用户不是公司法人
            // 检查是否授权给签署人权限
            $requestData = ['param' => ['filter' => [
                'user_id' => $signerUser['union_id'],
                'tenant_id' => $tenant['id'],
            ]]];
            $tenantGrant = teamones_request('teamones-im', 'tenant_grant/find', $requestData)['data'];
            if (empty($tenantGrant) || $tenantGrant['status'] != "active") {
                throw_strack_exception("you don`t have access right", ErrorCode::NO_ACCESS);
            }
            $requestData = ['param' => ['filter' => [
                'id' => $signerUser['union_id'],
            ]]];
            $remoteUser = teamones_request('teamones-im', 'user/find', $requestData)['data'];
            return $remoteUser['fdd_customer_id'];
        }
        return true;
    }

    /**
     * 框架合同默认合同时长配置
     * @return array
     */
    public function getFrameworkContractDateConfig()
    {
        $defaultDateLength = '1 year';
        $optionService = OptionsService::getInstance();
        $dateLength = $optionService->getOptionsConfigItemData('contract_config', 'framework_date_length', $defaultDateLength);
        return ['start' => time(), 'end' => strtotime('+ ' . $dateLength)];
    }

    /**
     * 添加订单签署人到项目成员
     * @param int $signerId
     * @param int $projectId
     * @return array
     * @throws \Exception
     */
    public function addOrderSignerToProjectMember(int $signerId, int $projectId)
    {
        $option = OptionsService::getInstance()->getOptionsData('project_initiation');
        $roleIdList = [];
        if (!empty($option['manage'])) {
            $roleIdList = explode(',', $option['manage']);
        }

        $roleList = [];
        if (!empty($roleIdList)) {
            $roleList = teamones_request('teamones-im', 'role/select', [
                'param' => [
                    'filter' => [
                        'role.id' => ['-in', $roleIdList]
                    ]
                ]
            ])['data']['rows'];
        }
        $roleIdMap = array_column($roleList, 'id', 'code');
        $roleCodeList = array_column($roleList, 'code');
        $project = model(ProjectModel::class)->field('tenant_id')->find($projectId);
        $projectMemberNeedAdd = [];

        if (in_array(TaskService::BUSINESS_PRODUCER_CODE, $roleCodeList)) {
            $userList = teamones_request('teamones-im', 'tenant/select_department_user_department_info_merged', ['param' => [
                'filter' => [
                    'role.code' => TaskService::BUSINESS_PRODUCER_CODE,
                    'tenant_id' => $project['tenant_id'],
                ]
            ]])['data']['rows'];
            $userIdList = array_column($userList, 'user_id', 'user_id');
            $userList = UserService::getInstance()->getUsersByUnionIds($userIdList);
            $role = teamones_request('teamones-im', 'role/find', ['param' => ['filter' => ['code' => TaskService::BUSINESS_PRODUCER_CODE]]])['data'];
            $stepCategoryData = model(StepCategoryModel::class)->field('id')->where(['code' => 'executive_producer'])->find();
            foreach ($userList as $user) {
                $teamId = 0;
                if (!empty($stepCategoryData['id'])) {
                    $teamId = $this->matchStepCategoryNameTeamData($role['id'], [$stepCategoryData['id']]);
                }
                $projectMemberNeedAdd[] = [
                    'user_id' => $user['id'],
                    'role_code' => $role['code'],
                    'role_id' => $role['id'],
                    'step_category_ids' => '',
                    'team_id' => $teamId
                ];
            }
            if (!empty($projectMemberNeedAdd)) {
                ProjectMemberService::getInstance()->addAll($projectMemberNeedAdd, ProjectMemberModel::MANAGE_TYPE, $projectId);
            }
        }

        $userService = UserService::getInstance();
        $projectCreator = model(ProjectModel::class)->field('created_by')->find($projectId);
        $userInfoList = $userService->getUsersByUserIds([$projectCreator['created_by'], $signerId], $project['tenant_id']);
        $userInfoList = array_column($userInfoList, null, 'saas_user_id');
        $res = [];
        $projectMemberNeedAdd = [];
        foreach ($userInfoList as $userInfo) {
            $roleCode = 'unknown';
            if (!empty($userInfo) && !empty($userInfo['role_info'])) {
                foreach ($userInfo['role_info'] as $role) {
                    if (empty($role['code'])) {
                        continue;
                    }
                    if (!in_array($role['code'], $roleCodeList)) {
                        continue;
                    }
                    $roleCode = $role['code'];
                    break;
                }
            }
            if (!in_array($roleCode, $roleCodeList)) {
                continue;
            }
            $projectMemberNeedAdd[] = [
                'user_id' => $userInfo['saas_user_id'],
                'role_code' => $roleCode,
                'role_id' => $roleIdMap[$roleCode],
                'step_category_ids' => '',
                'team_id' => 0
            ];
        }
        if (!empty($projectMemberNeedAdd)) {
            ProjectMemberService::getInstance()->addAll($projectMemberNeedAdd, ProjectMemberModel::MANAGE_TYPE, $projectId);
        }
        return $res;
    }

    /**
     * 匹配团队id
     * @param int $roleId
     * @param array $stepCategoryIdList
     * @return int
     */
    public function matchStepCategoryNameTeamData(int $roleId, array $stepCategoryIdList): int
    {
        $teamId = 0;
        if (empty($stepCategoryIdList) || empty($roleId)) {
            return $teamId;
        }
        $projectTeamConfig = OptionsService::getInstance()->getOptionsData('project_team_config');
        if (empty($projectTeamConfig)) {
            return $teamId;
        }
        foreach ($projectTeamConfig as $projectTeamConfigItem) {
            $configRoleIdIds = $projectTeamConfigItem['role_id'] . ',' . $projectTeamConfigItem['extra_role_id']; // 默认角色
            $configRoleIdList = explode(',', $configRoleIdIds);
            $configStepCategoryIdList = $projectTeamConfigItem['step_category_id'] ?? [];
            $configStepCategoryDiff = array_intersect($stepCategoryIdList, $configStepCategoryIdList);
            if (in_array($roleId, $configRoleIdList) && $configStepCategoryDiff) {
                $teamId = $projectTeamConfigItem['id'];
            }
        }
        return $teamId;
    }
}
